#include "stdafx.h"

/**
 * [ParamManager]
 *
 * This class is threadsafe.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ParamManager.h"

#ifdef _WIN32
#ifdef _DEBUG
	#define _CRTDBG_MAP_ALLOC
	#include <stdlib.h>
	#include <crtdbg.h>
	#undef THIS_FILE
	static char THIS_FILE[] = __FILE__;
	#define new       new( _NORMAL_BLOCK, __FILE__, __LINE__)
	#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#endif


namespace tlnet {

// Constructor
ParamManager::ParamManager(void) {
	strcpy_s(this->_default, 2, "");
	head = tail = curr = NULL;

	// by default overwrite is true
	this->isOverwrite = true;

	// create mutex in order to keep thread safe
	this->mutex = CreateMutex(NULL, FALSE, NULL/*LPCTSTR("NetworkManager")*/);
}

// Deconstructor
ParamManager::~ParamManager(void) {
	this->clear();
}


// Set whether to overwrite a data with the same name or not
void ParamManager::setOverwrite(bool isOverwrite) {
	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	this->isOverwrite = isOverwrite;

	// thread safe: end
	ReleaseMutex(this->mutex);
}

// Returns whether the data is set or not, needed because '->get' will return ""
// whether the data exists or not
bool ParamManager::isSet(char *data) {
	// special case, if the data is "" then return false, we never search for this
	// if needed, the data will have to be obtained by accessing the linked list directly
	if(strlen(data) == 0) {
		return false;
	}

	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	Param *slider = head;
	while(slider != NULL) {
		if(!strcmp(slider->data, data)) {
			// thread safe: end
			ReleaseMutex(this->mutex);

			return true;
		}

		slider = slider->next;
	}

	// thread safe: end
	ReleaseMutex(this->mutex);

	return false;
}

// Returns whether the data/value pair is set or not
bool ParamManager::isMatch(char *data, char *value) {
	// special case, if the data is "" then return false, we never search for this
	// if needed, the data will have to be obtained by accessing the linked list directly
	if(strlen(data) == 0) {
		return false;
	}

	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	Param *slider = head;
	while(slider != NULL) {
		if(!strcmp(slider->data, data)) {
			if(!strcmp(slider->value, value)) {
				// thread safe: end
				ReleaseMutex(this->mutex);

				return true;
			}
		}

		slider = slider->next;
	}

	// thread safe: end
	ReleaseMutex(this->mutex);

	return false;
}

// set a new data/value pair
// (isOverwrite determines if replace the data when already exists)
bool ParamManager::set(char *data, char *value) {
	// does the data already exist?
	if(isSet(data)) {
		if(isOverwrite) {
			// delete the data which already exists and use this new one instead
			this->del(data);
		}
	}

	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	// save the new data to a new struct
	Param *tmp = new Param;
	// set data/value
	tmp->data = _strdup(data);
	tmp->value = _strdup(value);

	// add to link list
	if(this->tail == NULL) {
		// set prev and next
		tmp->prev = tmp->next = NULL;
		// link list is empty, start one
		head = tail = tmp;
	}
	else {
		// set prev and next
		tmp->prev = tail;
		tmp->next = NULL;
		// tack on to end of list
		tail->next = tmp;
		tail = tmp;
	}

	// thread safe: end
	ReleaseMutex(this->mutex);

	return true;
}

// get a data's value
char* ParamManager::get(char *data) {
	// special case, if the data is "" then return default value, we never search for this
	// if needed, the data will have to be obtained by accessing the linked list directly
	if(strlen(data) == 0) {
		return this->_default;
	}

	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	Param *slider = head;
	while(slider != NULL) {
		if(!strcmp(slider->data, data)) {
			// thread safe: end
			ReleaseMutex(this->mutex);

			return slider->value;
		}

		slider = slider->next;
	}

	// thread safe: end
	ReleaseMutex(this->mutex);

	return this->_default;
}

// del a specified data
bool ParamManager::del(char *data) {
	// special case
	if(strlen(data) == 0) {
		return false;
	}

	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	// search for the data
	Param *slider = head;
	while(slider != NULL) {
		if(!strcmp(slider->data, data)) {
			// found it
			if(slider->prev != NULL) {
				slider->prev->next = slider->next;
			}
			else {
				head = slider->next;
				if(head) {
					head->prev = NULL;
				}
			}
			if(slider->next != NULL) {
				slider->next->prev = slider->prev;
			}
			else {
				tail = slider->prev;
				if(tail) {
					tail->next = NULL;
				}
			}

			if(slider->data != NULL) {
//				delete[] slider->data;
				free(slider->data);
			}
			if(slider->value != NULL) {
//				delete[] slider->value;
				free(slider->value);
			}
			delete slider;

			// thread safe: end
			ReleaseMutex(this->mutex);

			return true;
		}

		slider = slider->next;
	}

	// thread safe: end
	ReleaseMutex(this->mutex);

	return false;
}

// load up the passed in parameter string
// Example params = "a=1|b=2|c=3", delim = "|"
// Will generate the 3 name/value pairs
bool ParamManager::init(char *params, char *delim) {
	char *pTok, *pEqu;
	char *parseMe = _strdup(params);

	// clear out any currently stored data, if any
	this->clear();

	// thread safe: start
//	WaitForSingleObject(this->mutex, INFINITE);

	// get the next parameter
	pTok = strtok(parseMe, delim);
	while(pTok != NULL) {
		// Is there an '=' indicating a name/value parameter?
		pEqu = strstr(pTok, "=");
		if(pEqu == NULL) {
			// store name/value pair with name as blank
			this->set("", pTok);
		}
		else {
			// parse and save off a name/value pair
			pEqu[0] = 0;
			pEqu += strlen(delim);

			// store this next name/value pair
			this->set(pTok, pEqu);

			// restore the parameter for strtok
			pEqu -= strlen(delim);
			pEqu[0] = '=';
		}

		// pop off the next parameter
		pTok = strtok(NULL, delim);
	}

	// free up memory allocated with strdup
	free(parseMe);

	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	// initialize the curr pointer
	this->curr = this->head;

	// thread safe: end
	ReleaseMutex(this->mutex);

	return true;
}

// clear out the currently stored data
bool ParamManager::clear(void) {
	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	Param *slider = head;
	while(slider != NULL) {
		head = head->next;
		if(slider->data != NULL) {
//			delete[] slider->data;
			free(slider->data);
		}
		if(slider->value != NULL) {
//			delete[] slider->value;
			free(slider->value);
		}
		delete slider;

		slider = head;
	}

	head = tail = curr = NULL;

	// thread safe: end
	ReleaseMutex(this->mutex);

	// always returns true
	return true;
}

// get next param
Param* ParamManager::getNext(void) {
	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	Param *slider = this->curr;
	if(this->curr != NULL) {
		this->curr = this->curr->next;
	}

	// thread safe: end
	ReleaseMutex(this->mutex);

	return slider;
}

// reset looping of params back to begining
void ParamManager::reset(void) {
	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	this->curr = this->head;

	// thread safe: end
	ReleaseMutex(this->mutex);
}

// return number of param items
int ParamManager::getCount(void) {
	// thread safe: start
	WaitForSingleObject(this->mutex, INFINITE);

	Param *slider = this->head;
	int count = 0;

	while(slider != NULL) {
		count ++;

		slider = slider->next;
	}

	// thread safe: end
	ReleaseMutex(this->mutex);

	return count;
}

}